#include "L_System.h"
#include "Utils.h"

Turtle :: Turtle()
{
	X = 0;
	Y = 0;
	Z = 0;
	phi[0] = 0;
	phi[1] = 0;
	phi[2] = 0;
	lsys = nullptr;
	in3D = false;
}

Turtle :: Turtle(float x, float y, float p, L_System *l)
{
	X = x;
	Y = y;
	Z = 0;
	phi[0] = 0;
	phi[1] = 0;
	phi[2] = p;
	lsys = l;
	in3D = false;
}

Turtle :: Turtle(float x, float y, float z, float px, float py, float pz, L_System *l)
{
	X = x;
	Y = y;
	Z = z;
	phi[0] = px;
	phi[1] = py;
	phi[2] = pz;
	lsys = l;
	in3D = true;
}

void Turtle :: stir(std::string list)
{
	glTranslatef(1*X, 1*Y, 1*Z);
	glRotatef(phi[0], 1, 0, 0);
	glRotatef(phi[1], 0, 1, 0);
	glRotatef(phi[2], 0, 0, 1);

	for(std::string::iterator it = list.begin(); it != list.end(); it++)
	{
		switch(*it)				//Loop through our input list, applying appropriate
		{						//turtle behavior depending on the symbol received.
			case '&':
				glRotatef(lsys->theta, 1, 0, 0);
				break;
			case '^':
				glRotatef(-lsys->theta, 1, 0, 0);
			case '\\':
				glRotatef(lsys->theta, 0, 1, 0);
				break;
			case '/':
				glRotatef(-lsys->theta, 0, 1, 0);
				break;
			case '+':
				glRotatef(lsys->theta, 0, 0, 1);
				break;
			case '-':
				glRotatef(-lsys->theta, 0, 0, 1);
				break;
			case '|':
				glRotatef(180, 0, 0, 1);
				break;
			case '[':
				glPushMatrix();
				break;
			case ']':
				glPopMatrix();
				break;
			case '!':
			{
				/*GLfloat* width = (GLfloat*)malloc(sizeof(GLfloat));
				glGetFloatv(GL_LINE_WIDTH, width);
				glLineWidth(*width - 0.1f);
				delete(width);*/
			}
			case 'F':
			{
				
				if(in3D)
				{
					glScalef(lsys->edgeLength, lsys->edgeLength, lsys->edgeLength);
					drawLine3D();
					glScalef(1/lsys->edgeLength, 1/lsys->edgeLength, 1/lsys->edgeLength);
					glTranslatef(0, 0, lsys->edgeLength);
				}
				else
				{
					glScalef(lsys->edgeLength, lsys->edgeLength, 1);
					drawLine();
					glScalef(1/lsys->edgeLength, 1/lsys->edgeLength, 1);
					glTranslatef(0, lsys->edgeLength, 0);
				}

			}break;
			case 'L':
			{
				drawLeaf3D();
			}
		}
	}
}

Parametric_Turtle :: Parametric_Turtle()
{
	X = 0;
	Y = 0;
	Z = 0;
	phi[0] = 0;
	phi[1] = 0;
	phi[2] = 0;
	symbols = std::vector<Module>();
	lsys = nullptr;

	in3D = false;
}

Parametric_Turtle :: Parametric_Turtle(float x, float y, float p, L_System *l)
{
	X = x;
	Y = y;
	Z = 0;
	phi[0] = 0;
	phi[1] = 0;
	phi[2] = p;
	lsys = l;
	symbols = std::vector<Module>();
	in3D = false;
}

Parametric_Turtle :: Parametric_Turtle(float x, float y, float z, float px, float py, float pz, L_System *l)
{
	X = x;
	Y = y;
	Z = z;
	phi[0] = px;
	phi[1] = py;
	phi[2] = pz;
	lsys = l;
	symbols = std::vector<Module>();
	in3D = true;
}

void Parametric_Turtle :: stir(std::vector<Module> list)
{
	glTranslatef(1*X, 1*Y, 1*Z);
	glRotatef(phi[0], 1, 0, 0);
	glRotatef(phi[1], 0, 1, 0);
	glRotatef(phi[2], 0, 0, 1);

	for(int i = 0; i < list.size(); i++)
	{
		Module m = list[i];

		float deltaAngle;
		float deltaLength;
		
		if(!m.parameters.empty())
		{
			deltaAngle = m.parameters.front();		//For drawing and rotating we only take a single parameter, which will always be at the front
			deltaLength = m.parameters.front();
		}
		else
		{
			deltaAngle = lsys->theta;
			deltaLength = lsys->edgeLength;
		}

		switch(m.symbol)						//Loop through our input list, applying appropriate
		{										//turtle behavior depending on the symbol received.
			case '&':
				glRotatef(deltaAngle, 1, 0, 0);
				break;
			case '^':
				glRotatef(-deltaAngle, 1, 0, 0);
			case '\\':
				glRotatef(deltaAngle, 0, 1, 0);
				break;
			case '/':
				glRotatef(-deltaAngle, 0, 1, 0);
				break;
			case '+':
				glRotatef(deltaAngle, 0, 0, 1);
				break;
			case '-':
				glRotatef(-deltaAngle, 0, 0, 1);
				break;
			case '|':
				glRotatef(180, 0, 1, 0);
				break;
			case '[':
				glPushMatrix();
				break;
			case ']':
				glPopMatrix();
				break;
			case '!':
			{
				glLineWidth(deltaAngle);	//Just change the lineWidth based on the first parameter
			}
			break;
			case '$':
			{
				//This symbol should never be in a 2D L-System but this is what development is all about right? Covering your tracks
				if(in3D)
				{
					float *m = new float[16];
					glGetFloatv(GL_MODELVIEW_MATRIX, m);
					Vector heading = Vector(m[8], m[9], m[10]);
					//Vector up = Vector(m[4], m[5], m[6]);
					Vector left = Vector(m[0], m[1], m[2]);
					Vector oppGravity = Vector(0, 1, 0);				//Direction of not-gravity

					Vector newLeft = heading.Cross(oppGravity);				//The projected left direction
					newLeft.Normalize();
					left.Normalize();

					float rotationAngle = left.Dot(newLeft);	//Calculate the angle between the current and new left directions
					rotationAngle = acos(rotationAngle);		//Rotate about Z by that much

					//This operation is equivalent to rolling the turtle around its heading vector, which should be the same as a call to:
					glRotatef(rotationAngle, 0, 0, 1);
					//But we need to know how much to rotate it. The new vector should be equal to (V x H / |V x H|) where V is the direction
					//opposite gravity, so V = (0, 1, 0)
				}
			}
			break;
			case 'F':
			{
				
				if(in3D)
				{
					glScalef(deltaLength, deltaLength, deltaLength);
					drawLine3D();
					glScalef(1/deltaLength, 1/deltaLength, 1/deltaLength);
					glTranslatef(0, 0, deltaLength);
				}
				else
				{
					glScalef(deltaLength, deltaLength, 1);
					drawLine();
					glScalef(1/deltaLength, 1/deltaLength, 1);
					glTranslatef(0, deltaLength, 0);
				}

			}break;
			case 'L':
			{
				if(in3D)
				{
					glScalef(deltaLength, deltaLength, deltaLength);
					drawLeaf3D();
					glScalef(1/deltaLength, 1/deltaLength, 1/deltaLength);
				}
			}
		}
	}
}

//print matrix
/*std::cout << "\nSymbol: " << *it;
			/*std::cout << "\nX: " << X;
			std::cout << "\nY: " << Y;
			std::cout << "\nPhi: " << phi << "\n";

			/*glGetFloatv(GL_MODELVIEW_MATRIX, m);
					for(int i = 0; i < 4; i++)
					{
						std::cout << "\n";
						std::cout << m[i] << " ";
						std::cout << m[i+4] << " ";
						std::cout << m[i+8] << " ";
						std::cout << m[i+12] << " ";
					}
					std::cout << "\n";*/